/*
 * type_index without RTTI
 *
 * Copyright frickiericker 2016.
 * Distributed under the Boost Software License, Version 1.0.
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include <functional>

namespace ext {
/**
 * Dummy type for tag-dispatching.
 */
template <typename T>
struct tag_type {};

/**
 * A value of tag_type<T>.
 */
template <typename T>
constexpr tag_type<T> tag{};

/**
 * A type_index implementation without RTTI.
 */
struct type_index {
    /**
     * Creates a type_index object for the specified type.
     */
    template <typename T>
    type_index(tag_type<T>) noexcept : hash_code_{index<T>} {
    }

    /**
     * Returns the hash code.
     */
    std::size_t hash_code() const noexcept {
        return hash_code_;
    }

private:
    /**
     * Unique integral index associated to template type argument.
     */
    template <typename T>
    static std::size_t const index;

    /**
     * Global counter for generating index values.
     */
    static std::size_t& counter() noexcept {
        static std::size_t counter_;
        return counter_;
    }

private:
    std::size_t hash_code_;
};

template <typename>
std::size_t const type_index::index = type_index::counter()++;

/**
 * Creates a type_index object for the specified type.
 *
 * Equivalent to `ext::type_index{ext::tag<T>}`.
 */
template <typename T>
type_index make_type_index() noexcept {
    return tag<T>;
}

inline bool operator==(type_index const& a, type_index const& b) noexcept {
    return a.hash_code() == b.hash_code();
}

inline bool operator!=(type_index const& a, type_index const& b) noexcept {
    return !(a == b);
}

inline bool operator<(type_index const& a, type_index const& b) noexcept {
    return a.hash_code() < b.hash_code();
}

inline bool operator<=(type_index const& a, type_index const& b) noexcept {
    return a.hash_code() <= b.hash_code();
}

inline bool operator>(type_index const& a, type_index const& b) noexcept {
    return !(a <= b);
}

inline bool operator>=(type_index const& a, type_index const& b) noexcept {
    return !(a < b);
}
}

template <>
struct std::hash<ext::type_index> {
    using argument_type = ext::type_index;
    using result_type = std::size_t;

    result_type operator()(argument_type const& t) const noexcept {
        return t.hash_code();
    }
};
